﻿/*
*********************************************************************************************************
*                                                uC/GUI
*                        Universal graphic software for embedded applications
*
*                       (c) Copyright 2002, Micrium Inc., Weston, FL
*                       (c) Copyright 2002, SEGGER Microcontroller Systeme GmbH
*
*              礐/GUI is protected by international copyright laws. Knowledge of the
*              source code may not be used to write a similar product. This file may
*              only be used in accordance with a license and should not be redistributed
*              in any way. We appreciate your understanding and fairness.
*
----------------------------------------------------------------------
File        : WinMain.c
Purpose     : Windows Simulator, main program
---------------------------END-OF-HEADER------------------------------
*/

#include <windows.h>
#include <stdio.h>
#include "ResourceSim.h"
#include "LCD.h"
#include "GUI.h"
#include "SIM.h"
#include "LCDSIM.h"
#include "Branding\Branding.h"
#include "LCDSIM_Private.h"

/*********************************************************************
*
*       Defines
*
**********************************************************************
*/

#ifndef SIM_WINMAIN
#define SIM_WINMAIN 1
#endif

#define COUNT_OF(exp) (sizeof(exp)/sizeof(exp[0]))
#define HARDKEYS_MAX   128
#define LOG_COLOR_RED 1

/*********************************************************************
*
*       Typedefs
*
**********************************************************************
*/

typedef void TaskMain(void);

// **************************************************************************
// 内部数据

typedef struct {
    int VKey;
    int Key;
} VKEY2KEY;

// win平台虚拟按键 gui按键映射表
const VKEY2KEY aVKey2Key[] = {
  {VK_UP,         GUI_KEY_UP},
  {VK_DOWN,       GUI_KEY_DOWN},
  {VK_RIGHT,      GUI_KEY_RIGHT},
  {VK_LEFT,       GUI_KEY_LEFT},
  {VK_HOME,       GUI_KEY_HOME},
  {VK_END,        GUI_KEY_END},
  {VK_SHIFT,      GUI_KEY_SHIFT},
  {VK_CONTROL,    GUI_KEY_CONTROL},
  {VK_BACK,       GUI_KEY_BACKSPACE},
  {VK_INSERT,     GUI_KEY_INSERT},
  {VK_DELETE,     GUI_KEY_DELETE},
};


// **************************************************************************
// 内部静态变量

static int       _xPosLCD = -1;     // LCD显示位置x坐标
static int       _yPosLCD = -1;     // LCD显示位置y坐标
static int       _rgbTransparent;   // 透明色
static int       _MagX = 1;          // x方向放大倍数
static int       _MagY = 1;          // y方向放大倍数
static char      _MessageBoxOnError = 1;    //发生错误时弹出对话框
static HANDLE    _hLogo;
static HBITMAP   _ahBmpDevice[2];
static int       _aLUTModifyCnt[LCDSIM_MAX_DISPLAYS];
static int       _aModifyCnt[LCDSIM_MAX_DISPLAYS];
static struct {
    int x0, y0, x1, y1;
    char IsPressed;
    SIM_HARDKEY_CB* pfCallback;
    int Mode;
} _aHardkey[HARDKEYS_MAX];


static int       _NumHardkeys;

static HANDLE    _hFileError;
static HINSTANCE _hInst;                                    // current instance
static HWND      _hWndMain;     // 主框架窗口
//static HWND      _hParent;
static HWND      _hWndLCD1;     // LCD模拟窗口
static char      _KeyBuffer;
static char      _CmdKill;
static char *    _pCmdLine;     // 命令行
static DWORD     _SuspendCount; // 线程挂起计数
static int       _timeStartup;
static HMENU     _hMenuPopup;

// 窗口类字符串
//
static const char acClassNameMain[] = "uC-GUI Simulation Mainframe";
static const char acClassNameLCDInfo[] = "uC-GUI LCD Info";
static const char acClassNameLCD[] = "uC-GUI LCD";
static const char acClassNameDevice[] = "uC-GUIDevice";
static const char acClassNameHardkey[] = "uC-GUIHardkey";

// 线程管理句柄
//
static HANDLE _ghThread = NULL;

/* multitasking support */
static U8     _NumTask;
static U32    _aThreadID[100];
static HANDLE _ahThread[100];

/* LCD-window */
typedef struct {
    HWND hWnd;
    int  ModifyCnt;
} LCD_WINDATA;

/*********************************************************************
*
*       Forward declarations
*
**********************************************************************
*/

void main(void);
void MainTask(void);
static LRESULT CALLBACK _WndProcMain(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK _WndProcLCD(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK _WndProcDevice(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK _WndProcLCDInfo(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK _WndProcHardkey(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);


// **************************************************************************
// 外部函数引用

void WinLOG_Init(HINSTANCE hInst, HICON hIcon);
void WinLOG_Create(HINSTANCE hInst, HWND hWndParent, int y0);
void __cdecl _LOG_Add(const char *format, ...);
void _LOG_AddRed(void);
void _LOG_Clear(void);


// **************************************************************************
// 静态函数

// 将win平台虚拟按键转换为gui按键
static int _VirtKey2Key(int VirtKey) {
    int i;
    for (i = 0; aVKey2Key[i].VKey; i++) {
        if (aVKey2Key[i].VKey == VirtKey) {
            return aVKey2Key[i].Key;
        }
    }
    return 0;
}

/*********************************************************************
*
*       _Keydown2ASCII
*/
static U16 _Keydown2ASCII(WPARAM wParam) {
    int Ret;
    U8 aState[256];
    U16 Key;
    GetKeyboardState(aState);
    Ret = ToAscii(wParam, MapVirtualKey(wParam, 0), aState, &Key, 0);
    if (Ret == 1) {
        return Key & 255;
    }
    return (U16)wParam;
}

/*********************************************************************
*
*       _MessageBox1
*/
static void _MessageBox(LPCTSTR lpText) {
    MessageBox(_hWndMain, lpText, BRANDING_GetAppNameShort(), MB_OK | MB_ICONEXCLAMATION);
}

/*********************************************************************
*
*       _LinesIntersect
*/
static int _LinesIntersect(int xLeft0, int xRight0, int xLeft1, int xRight1) {
    int x0 = max(xLeft0, xLeft1);
    int x1 = min(xRight0, xRight1);
    return (x1 - x0 >= 0);
}

/*********************************************************************
*
*       _GetWidthOfBitmap
*/
static int _GetWidthOfBitmap(HBITMAP hBmp) {
    BITMAP bmp;
    GetObject(hBmp, sizeof(bmp), &bmp);
    return bmp.bmWidth;
}

/*********************************************************************
*
*       _GetYSizeOfBitmap
*/
static int _GetYSizeOfBitmap(HBITMAP hBmp) {
    BITMAP bmp;
    GetObject(hBmp, sizeof(bmp), &bmp);
    return bmp.bmHeight;
}

/*********************************************************************
*
*       _GetScanLine
*/
static void _GetScanLine(HBITMAP hBmp, int y, DWORD* pRGB) {
    if (hBmp) {
        HDC hdc;
        BITMAPV4HEADER BmpInfo = { 0 };
        BmpInfo.bV4Size = sizeof(BmpInfo);
        hdc = GetWindowDC(NULL);
        /* Fill in the Bitmap info structure */
        GetDIBits(hdc, hBmp, 0, 0, NULL, (BITMAPINFO*)&BmpInfo, DIB_RGB_COLORS);
        BmpInfo.bV4V4Compression = BI_RGB;
        BmpInfo.bV4BitCount = 32;
        if (BmpInfo.bV4Height > 0)
            y = BmpInfo.bV4Height - y - 1;
        GetDIBits(hdc, hBmp, y, 1, pRGB, (BITMAPINFO*)&BmpInfo, DIB_RGB_COLORS);
    }
}

/*********************************************************************
*
*       _CompareBits
*/
static int _CompareBits(U32 Color0, U32 Color1) {
    if ((Color0 & 0xffffff) == (Color1 & 0xffffff)) {
        return 0;
    }
    return 1;
}


/*********************************************************************
*
*       _SetBitmapRegion

This is a key function for the display of the bitmap. It assigns
a clipping region to the window identical to the outline of the
bitmap.

*/
static void _SetBitmapRegion(HWND hWnd, HBITMAP hBmp, DWORD rgbTrans, char IsHardkey) {
    if (hBmp) {
        HRGN hRgn = 0;
        HRGN hRgn0;
        POINT Point = { 0 };
        DWORD acBits[4096];
        int y;
        int yOff = 0;
        int xOff = 0;
        int XSize = _GetWidthOfBitmap(hBmp);
        int YSize = _GetYSizeOfBitmap(hBmp);
        RECT WinRect;
        if (IsHardkey) {
            HWND hWndParent = GetParent(hWnd);
            ClientToScreen(hWndParent, &Point);
            GetWindowRect(hWnd, &WinRect);
            YSize = WinRect.bottom - WinRect.top;
            yOff = WinRect.top - Point.y;
            xOff = WinRect.left - Point.x;
        }
        for (y = 0; y < YSize; y++) {
            int i, i0, i1;
            _GetScanLine(hBmp, y + yOff, &acBits[0]);
            for (i = 0; i < XSize;) {
                while (_CompareBits(acBits[i], rgbTrans) == 0 && i < XSize) {
                    i++;
                }
                i0 = i;
                while (_CompareBits(acBits[i], rgbTrans) != 0 && i < XSize) {
                    i++;
                }
                i1 = i;
                if (i0 < XSize) {
                    hRgn0 = CreateRectRgn(i0 - xOff, y, i1 - xOff, y + 1);
                    if (hRgn0) {
                        if (hRgn) {
                            CombineRgn(hRgn, hRgn, hRgn0, RGN_OR);
                            DeleteObject(hRgn0);
                        }
                        else {
                            hRgn = hRgn0;
                        }
                    }
                    else {
                        _MessageBox("Could not create region");
                    }
                }
            }
        }
        SetWindowRgn(hWnd, hRgn, 0);  // Note: Region is now owned by the system, do not delete
    }
}

/*********************************************************************
*
*       Find hardkeys
*/
static void _AddHardkeyRegion(int y, int x0, int x1) {
    //
    // Find a hardkey that we can attach this segment to
    //
    int i;
    for (i = 0; i < COUNT_OF(_aHardkey); i++) {
        if (x1 > x0) {
            if (_aHardkey[i].y1 + 1 == y) {
                if (_LinesIntersect(x0, x1, _aHardkey[i].x0, _aHardkey[i].x1)) {
                    _aHardkey[i].y1++;
                    _aHardkey[i].x0 = min(_aHardkey[i].x0, x0);
                    _aHardkey[i].x1 = max(_aHardkey[i].x1, x1);
                    return; // We are done since there can be only one Rect
                }
            }
        }
    }
    //
    // Find a free hardkey
    //
    for (i = 0; i < COUNT_OF(_aHardkey); i++) {
        if ((_aHardkey[i].x1 == 0) && (_aHardkey[i].x0 == 0)) {
            _aHardkey[i].x0 = x0;
            _aHardkey[i].x1 = x1;
            _aHardkey[i].y0 = y;
            _aHardkey[i].y1 = y;
            _NumHardkeys++;
            return;
        }
    }
    SIM_ErrorOut("_AddHardkeyRegion: - Too many hardkeys defined in bitmap");
}

/*********************************************************************
*
*       _FindHardkeys
*/
static void _FindHardkeys(HBITMAP hBmp, DWORD rgbTrans) {
    DWORD aColor[4096];
    if (hBmp) {
        int x, y;
        int XSize = _GetWidthOfBitmap(hBmp);
        int yMax = _GetYSizeOfBitmap(hBmp);
        for (y = 0; y < yMax; y++) {
            int x0, x1;
            _GetScanLine(hBmp, y, &aColor[0]);
            for (x = 0; x < XSize;) {
                while ((_CompareBits(aColor[x], rgbTrans) == 0) && (x < XSize)) {
                    x++;
                }
                x0 = x;
                while ((_CompareBits(aColor[x], rgbTrans) != 0) && (x < XSize)) {
                    x++;
                }
                x1 = x;
                if (x1 > x0) {
                    _AddHardkeyRegion(y, x0, x1);
                }
            }
        }
    }
}

// 获取y方向物理尺寸
static int _GetYSizePhysEx(int LayerIndex) {
    return LCD_GetYSizeEx(LayerIndex) * LCD_GetYMagEx(LayerIndex);
}

// 获取x方向物理尺寸
static int _GetXSizePhysEx(int LayerIndex) {
    return LCD_GetXSizeEx(LayerIndex) * LCD_GetXMagEx(LayerIndex);
}

/*********************************************************************
*
*       _GetXSizePhys, _GetYSizePhys
*/
static int _GetYSizePhys(void) { return _GetYSizePhysEx(0); }
static int _GetXSizePhys(void) { return _GetXSizePhysEx(0); }

// **************************************************************************
// 内部函数

//! \brief    剪切板支持
//! \details  将当前界面截图复制到剪切板中
//! \param[in]  LayerIndex   当前层
//! \return   void
static void _OnCopy(int LayerIndex) {
    HGLOBAL hMemClipboard;
    BITMAPINFOHEADER bmiHeader = { 0 };
    int XSize = _GetXSizePhysEx(LayerIndex);    //x方向物理尺寸
    int YSize = _GetYSizePhysEx(LayerIndex);    //y方向物理尺寸
    int BPP = LCD_GetBitsPerPixel_L0Ex(LayerIndex); //每像素位数
    int x, y, i, Size;
    //每行字节数以4字节对齐
    int BytesPerLine = ((BPP > 8) ? (2 * XSize + 2) : (XSize + 3)) & ~3;
    int NumColors = (BPP > 8) ? 0 : (1 << BPP);
    union {
        U8*               u8;
        U16*              u16;
        COLORREF*         ColorRef;
        BITMAPINFOHEADER* BitmapInfoHeader;
    } p;
    Size = sizeof(BITMAPINFOHEADER) + NumColors * 4 + YSize * BytesPerLine;
    hMemClipboard = GlobalAlloc(GMEM_MOVEABLE | GMEM_DDESHARE, Size);  // Note that GlobalFree is called automatically by windows
    p.u8 = GlobalLock(hMemClipboard);
    bmiHeader.biClrUsed = NumColors;
    bmiHeader.biBitCount = (BPP <= 8) ? 8 : 16;
    bmiHeader.biHeight = YSize;
    bmiHeader.biPlanes = 1;
    bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    bmiHeader.biWidth = XSize;
    //填充位图信息头数据
    *p.BitmapInfoHeader++ = bmiHeader;
    //填充调色板数据
    for (i = 0; i < NumColors; i++) {
        COLORREF Color;
        Color = LCDSIM_Index2Color(i, LayerIndex);
        Color = ((Color >> 16) & 255) | (Color & 0xff00) | ((Color & 0xff) << 16);
        *p.ColorRef++ = Color;
    }
    //填充像素数据
    for (y = YSize - 1; y >= 0; y--) {
        for (x = 0; x < XSize; x++) {
            if (BPP <= 8) {
                *p.u8++ = LCDSIM_GetPixelIndex(x, y, LayerIndex);
            }
            else {
                int Color = LCDSIM_GetPixelColor(x, y, LayerIndex);
                int r = ((Color >> 16) * 31 + 127) / 255;
                int g = (((Color >> 8) & 255) * 31 + 127) / 255;
                int b = ((Color & 255) * 31 + 127) / 255;
                *p.u16++ = (r << 10) | (g << 5) | b;   // 16 bpp Bitmaps in windows are 555: rrrrrgggggbbbbb
            }
        }
        //对齐指针到下个u32类型
        if (BytesPerLine & 3)
            p.u8 += 3 - (BytesPerLine & 3);
    }
    //拷贝到剪切板
    OpenClipboard(NULL);
    EmptyClipboard();
    SetClipboardData(CF_DIB, hMemClipboard);
    CloseClipboard();
    GlobalUnlock(hMemClipboard);
}

/*********************************************************************
*
*       Static functions: Thread management
*
**********************************************************************
*/
/*********************************************************************
*
*       _Thread
*/
// 主任务线程
//
// 程序新建并运行该线程以运行GUI Demo程序
static DWORD __stdcall _Thread(void* Parameter) {
#ifdef WATCOM
    main();
#else
    MainTask();
#endif
    _ghThread = 0;
    SIM_Log("\nApplication terminated.");
    return 0;
}

/*********************************************************************
*
*       _THREAD_IsRunning
*/
static char _THREAD_IsRunning(void) {
    return _ghThread ? 1 : 0;
}

/*********************************************************************
*
*       _THREAD_IsSuspended
*/
static char _THREAD_IsSuspended(void) {
    return _SuspendCount ? 1 : 0;
}

/*********************************************************************
*
*       _THREAD_KillAll
*/
static void _THREAD_KillAll(void) {
    // Try to get the thread to terminate by itself
    int i = 0;
    _CmdKill = 1;
    while (_ghThread && i++ < 20)
        Sleep(10);
    if (_ghThread)
        TerminateThread(_ghThread, 0);
    _ghThread = 0;
    _CmdKill = 0;
}

//! \brief     线程休眠
//!
//! \param[in] ms       休眠时间(毫秒)
//! \return    void
static void _THREAD_Sleep(int ms) {
    while (ms > 0) {
        if (_CmdKill) {
            //暂时增加线程的优先级以确保其在退出前不会被中断
            SetThreadPriority(_ghThread, THREAD_PRIORITY_HIGHEST);
            _ghThread = 0;
            ExitThread(0);
        }
        //以10mS为单位休眠
        Sleep(10); ms -= 10;
    }
}

//! \brief      启动演示应用程序线程
//! \details    调用该函数将创建演示程序线程并运行之
//!
//! \return     void
static void _THREAD_StartApplication(void) {
    DWORD ThreadId;
    _LOG_Clear();
    SIM_Log("Application started");
    //启动前先结束所有现有线程
    if (_ghThread)
        _THREAD_KillAll();
    //创建并运行线程
    _ghThread = CreateThread(NULL, 0, _Thread, NULL, 0, &ThreadId);
    _SuspendCount = 0;
}

//! \brief      停止应用程序
//! \details    调用该函数将挂起应用程序线程运行
//!
//! \return     void
static void _THREAD_StopApplication(void) {
    int i;
    SIM_Log("\nApplication suspended");
    if (_SuspendCount) {
        _MessageBox("Application paused already ...");
        return;
    }
    if (!_ghThread) {
        _MessageBox("Application is not running...");
        return;
    }
    //挂起运行的线程
    _SuspendCount = SuspendThread(_ghThread) + 1;
    for (i = 0; i < _NumTask; i++) {
        SuspendThread(_ahThread[i]);
    }
}

/*********************************************************************
*
*       _THREAD_ContinueApplication
*/
static void _THREAD_ContinueApplication(void) {
    int i;
    SIM_Log("\nApplication continued");
    if (!_SuspendCount) {
        _MessageBox("Application is not stopped...");
        return;
    }
    if (!_ghThread) {
        _MessageBox("Application is not running...");
        return;
    }
    _SuspendCount = ResumeThread(_ghThread) - 1;
    for (i = 0; i < _NumTask; i++) {
        ResumeThread(_ahThread[i]);
    }
}

/*********************************************************************
*
*       _CreateTask
*/
static unsigned long __stdcall _CreateTask(LPVOID lpParameter) {
    ((TaskMain*)lpParameter)();
    return 0;
}

// **************************************************************************
// 内部函数

// 在日志中记录时间信息
//
static void _LogTime(void) {
    char ac[80];
    sprintf(ac, "\n%d:   ", SIM_GetTime());
    _LOG_Add(ac);
}

/*********************************************************************
*
*       _SendToErrorFile
*/
static void _SendToErrorFile(const char* s) {
    DWORD NumBytesWritten;
    if (_hFileError == 0) {
        _hFileError = CreateFile("SimError.log", GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
    }
    WriteFile(_hFileError, "\r\n", 2, &NumBytesWritten, NULL);
    WriteFile(_hFileError, s, strlen(s), &NumBytesWritten, NULL);
}

//! \brief      注册窗口类
//! \details    调用该函数将窗口类注册到windows系统中
//!
//! \return     void
static void _RegisterClasses(void) {
    WNDCLASSEX wcex;
    HICON hIcon = LoadIcon(_hInst, (LPCTSTR)IDR_MAINFRAME);
    memset(&wcex, 0, sizeof(wcex));
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.hInstance = _hInst;
    //
    // 注册主窗口
    //
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = (WNDPROC)_WndProcMain;
    wcex.hIcon = LoadIcon(_hInst, (LPCTSTR)IDR_MAINFRAME);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
    wcex.lpszMenuName = (LPCSTR)IDC_SIMULATION;     // 主界面菜单资源ID
    wcex.lpszClassName = acClassNameMain;
    RegisterClassEx(&wcex);
    //
    // 注册设备窗口
    //
    wcex.lpfnWndProc = (WNDPROC)_WndProcDevice;
    wcex.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = acClassNameDevice;
    RegisterClassEx(&wcex);
    //
    // 注册LCD信息窗口
    //
    wcex.style = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc = (WNDPROC)_WndProcLCDInfo;
    wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wcex.lpszClassName = acClassNameLCDInfo;
    RegisterClassEx(&wcex);//
    //
    // Register Hardkey window
    //
    wcex.style = 0;
    wcex.lpfnWndProc = (WNDPROC)_WndProcHardkey;
    wcex.lpszClassName = acClassNameHardkey;
    RegisterClassEx(&wcex);
    //
    // 注册LCD窗口
    //
    wcex.lpfnWndProc = (WNDPROC)_WndProcLCD;
    wcex.lpszClassName = acClassNameLCD;
    RegisterClassEx(&wcex);//
    //
    // 注册log窗口
    //
    WinLOG_Init(_hInst, hIcon);
}

/*********************************************************************
*
*       _WndProcLCD
*/
static LRESULT CALLBACK _WndProcLCD(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    int xPos = (signed short)LOWORD(lParam) / _MagX;  // 光标水平位置
    int yPos = (signed short)HIWORD(lParam) / _MagY;  // 光标垂直位置 
    unsigned int LayerIndex = GetWindowLong(hWnd, GWL_USERDATA);
    switch (message) {
    case WM_CREATE:
        SetTimer(hWnd, 0, 20, NULL);
        break;
    case WM_TIMER:
        if (LayerIndex == 0) {
            LCDSIM_CheckMouseState();
        }
        if (LayerIndex < COUNT_OF(_aModifyCnt)) {
            int NewCnt = LCDSIM_GetModifyCnt(LayerIndex);
            if (_aModifyCnt[LayerIndex] != NewCnt) {
                if (InvalidateRect(hWnd, NULL, FALSE)) {
                    _aModifyCnt[LayerIndex] = NewCnt;            // invalidation successfull
                }
            }
        }
        if (_ghThread == NULL)
            PostQuitMessage(0);
        break;
    case WM_PAINT:
        LCDSIM_Paint(hWnd);
        break;
        // Handle mouse events
    case WM_RBUTTONDOWN:
    { POINT Point;
    Point.x = (signed short)LOWORD(lParam);
    Point.y = (signed short)HIWORD(lParam);
    ClientToScreen(hWnd, &Point);
    TrackPopupMenu(_hMenuPopup, TPM_RIGHTBUTTON, Point.x, Point.y, 0, GetParent(hWnd), NULL);
    }
    break;
    case WM_LBUTTONUP:
    case WM_LBUTTONDOWN:
    case WM_MOUSEMOVE:
    {
        int fwKeys = wParam;        // key flags 
        if ((xPos < 0) | (yPos < 0)
            | (xPos > LCD_GetDevCap(LCD_DEVCAP_XSIZE))
            | (yPos > LCD_GetDevCap(LCD_DEVCAP_YSIZE)))
        {
            xPos = -1;
            yPos = -1;
            fwKeys = 0;
        }
        LCDSIM_SetMouseState(xPos, yPos, fwKeys);
    }
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

/*********************************************************************
*
*       _CreateWndLCDInfo
*/
static void _CreateWndLCDInfo(int LayerIndex) {
    int NumColors, DeltaMode;
    int x, y;
    char ac[80];
    HWND hWnd;
    int xSizeFrame = GetSystemMetrics(SM_CXSIZEFRAME);
    int ySizeFrame = GetSystemMetrics(SM_CYSIZEFRAME);
    int ySizeCaption = GetSystemMetrics(SM_CYCAPTION);
    DeltaMode = LCD_GetDeltaModeEx(LayerIndex);
    x = _GetXSizePhys() + xSizeFrame * 2;
    y = LayerIndex * 100;
    wsprintf(ac, "Colors #%d", LayerIndex);
    if (DeltaMode) {
        NumColors = 1 << LCD_GetBitsPerPixel_L0Ex(LayerIndex);
    }
    else {
        NumColors = LCD_GetNumColorsEx(LayerIndex);
    }
    if (NumColors > 256) {
        hWnd = CreateWindow(acClassNameLCDInfo, ac,
            WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
            | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CHILD | WS_CLIPSIBLINGS,
            x, y,
            128 + xSizeFrame * 2,
            128 + ySizeFrame * 2 + ySizeCaption,
            _hWndMain, NULL, _hInst, NULL);
    }
    else {
        int ysize;
        ysize = (NumColors + 15) / 16 * 10 + 30;
        hWnd = CreateWindow(acClassNameLCDInfo, ac,
            WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
            | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CHILD | WS_CLIPSIBLINGS,
            x, y,
            160 + 10,
            ysize,
            _hWndMain, NULL, _hInst, NULL);
        SetTimer(hWnd, 0, 20, NULL);
    }
    SetWindowLong(hWnd, GWL_USERDATA, LayerIndex);
    ShowWindow(hWnd, SW_SHOW);
}

// 创建LCD模拟窗口
static void _CreateWndLCD(void) {
    int  i;
    int  NumDisplays = LCD_GetNumLayers();
    int  xSizeFrame = GetSystemMetrics(SM_CXSIZEFRAME);
    int  ySizeFrame = GetSystemMetrics(SM_CYSIZEFRAME);
    int  ySizeCaption = GetSystemMetrics(SM_CYCAPTION);
    char acTitle[200];
    for (i = 0; i < NumDisplays; i++) {
        int  XSize = _GetXSizePhysEx(i);
        int  YSize = _GetYSizePhysEx(i);
        int  BPP = LCD_GetBitsPerPixel_L0Ex(i);
        int  FixedPalette = LCD_GetFixedPaletteEx(i);
        HWND hWnd;
        wsprintf(acTitle, "LCD #%d %d*%d %dbpp, FixedPalette %d", i, XSize, YSize, BPP, FixedPalette);
        hWnd = CreateWindow(acClassNameLCD, acTitle,
            WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
            | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_CHILD | WS_CLIPSIBLINGS,
            20 * i, 20 * i, XSize * _MagX + 2 * xSizeFrame, YSize * _MagY + ySizeCaption + 2 * ySizeFrame,
            _hWndMain, NULL, _hInst, NULL);
        SetWindowLong(hWnd, GWL_USERDATA, i);
        ShowWindow(hWnd, SW_SHOW);
        _CreateWndLCDInfo(i);
    }
}

// 关于对话框
// 
static LRESULT CALLBACK _About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WM_INITDIALOG:
    {
        char acBuffer[80];
        sprintf(acBuffer, "About %s", BRANDING_GetAppNameLong());
        SetWindowText(hDlg, acBuffer);
        SetDlgItemText(hDlg, IDC_APPNAME, BRANDING_GetAppNameShort());
        sprintf(acBuffer, "Version: %s", GUI_GetVersionString());
        SetDlgItemText(hDlg, IDC_VERSION, acBuffer);
        SetDlgItemText(hDlg, IDC_COPYRIGHT, BRANDING_GetCopyright());
    }
    return TRUE;
    case WM_COMMAND:
        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
            EndDialog(hDlg, LOWORD(wParam));
            return TRUE;
        }
        break;
    }
    return FALSE;
}

// 主窗口命令处理历程
//
static int _MainWnd_Command(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    int wmId = LOWORD(wParam);
    //  int wmEvent = HIWORD(wParam);
    // Parse the menu selections:
    switch (wmId) {
    case ID_EDIT_COPY:                _OnCopy(0);                                                          break;  // TBD: Use LayerIndex
    case ID_VIEW_LCD:                 _CreateWndLCD();                                                     break;
    case ID_VIEW_LCDINFO:             _CreateWndLCDInfo(0);                                                break;
    case IDM_ABOUT:                   DialogBox(_hInst, (LPCTSTR)IDD_ABOUTBOX, _hWndMain, (DLGPROC)_About); break;
    case IDM_EXIT:                    DestroyWindow(hWnd);                                                 break;
    case ID_FILE_STARTAPPLICATION:    _THREAD_StartApplication();                                          break;
    case ID_FILE_STOPAPPLICATION:     _THREAD_StopApplication();                                           break;
    case ID_FILE_CONTINUEAPPLICATION: _THREAD_ContinueApplication();                                       break;
    case ID_VIEW_LOG:                 WinLOG_Create(_hInst, hWnd, 0);                                           break;
    default: return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// key按键事件处理
void _HandleKeyEvents(UINT Msg, WPARAM wParam) {
    int Key;
    switch (Msg) {
    case WM_KEYUP:
        Key = _VirtKey2Key(wParam);
        if (Key) {
            GUI_StoreKeyMsg(Key, 0);
        }
        else {
            if ((Key = _Keydown2ASCII(wParam)) != 0) {
                GUI_StoreKeyMsg(Key, 0);
            }
        }
        break;
    case WM_KEYDOWN:
        Key = _VirtKey2Key(wParam);
        if (Key) {
            GUI_StoreKeyMsg(Key, 1);
        }
        else {
            if ((Key = _Keydown2ASCII(wParam)) != 0) {
                GUI_StoreKeyMsg(Key, 1);
            }
        }
        break;
    }
}

/*********************************************************************
*
*       _WndProcDevice
*/
static LRESULT CALLBACK _WndProcDevice(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    HDC hdc;
    PAINTSTRUCT ps;
    RECT r;
    static int CaptureOn = 0;
    static int xPosOld, yPosOld;
    int xPos = (signed short)LOWORD(lParam);  // horizontal position of cursor
    int yPos = (signed short)HIWORD(lParam);  // vertical position of cursor
    _HandleKeyEvents(message, wParam);
    switch (message) {
    case WM_CREATE:
        CreateWindow(acClassNameLCD, "LCD window", WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE,
            _xPosLCD, _yPosLCD, _GetXSizePhys() * _MagX, _GetYSizePhys() * _MagY,
            hWnd, NULL, _hInst, NULL);
        _SetBitmapRegion(hWnd, _ahBmpDevice[0], _rgbTransparent, 0);
        _FindHardkeys(_ahBmpDevice[1], _rgbTransparent);
        { int i;
        for (i = 0; i < COUNT_OF(_aHardkey); i++) {
            if ((_aHardkey[i].x1 > _aHardkey[i].x0) && (_aHardkey[i].y1 > _aHardkey[i].y0)) {
                HWND hWndHardkey = CreateWindow(acClassNameHardkey, "Hardkey", WS_CHILD | WS_CLIPSIBLINGS
                    | WS_VISIBLE,
                    _aHardkey[i].x0, _aHardkey[i].y0,
                    _aHardkey[i].x1 - _aHardkey[i].x0 + 1,
                    _aHardkey[i].y1 - _aHardkey[i].y0 + 1,
                    hWnd, NULL, _hInst, NULL);
                SetWindowLong(hWndHardkey, GWL_USERDATA, i);
            }
        }
        }
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps); {
            HDC hdcImage = CreateCompatibleDC(hdc); {
                SelectObject(hdcImage, _ahBmpDevice[0]);
                BitBlt(hdc, 0, 0, 1000, 1000, hdcImage, 0, 0, SRCCOPY);
            } DeleteDC(hdcImage);
        } EndPaint(hWnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
        // Handle mouse events
    case WM_RBUTTONDOWN:
    { POINT Point;
    Point.x = xPos;
    Point.y = yPos;
    ClientToScreen(hWnd, &Point);
    TrackPopupMenu(_hMenuPopup, TPM_RIGHTBUTTON, Point.x, Point.y, 0, hWnd, NULL);
    }
    break;
    // Handle mouse events
    case WM_LBUTTONDOWN:
        SetCapture(hWnd);
        CaptureOn = 1;
        xPosOld = xPos;
        yPosOld = yPos;
        break;
    case WM_LBUTTONUP:
        ReleaseCapture();
        CaptureOn = 0;
        break;
    case WM_COMMAND:
        return _MainWnd_Command(hWnd, message, wParam, lParam);
    case WM_MOUSEMOVE:
        GetWindowRect(hWnd, &r);
        if (CaptureOn) {
            int xDiff, yDiff;
            xDiff = xPos - xPosOld;
            yDiff = yPos - yPosOld;
            if (xDiff | yDiff) {
                MoveWindow(hWnd, r.left + xDiff, r.top + yDiff, r.right - r.left, r.bottom - r.top, 1);
            }
        }
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

/*********************************************************************
*
*       _WndProcHardkey
*/
static LRESULT CALLBACK _WndProcHardkey(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    HDC hdc;
    PAINTSTRUCT ps;
    int xPos = (signed short)LOWORD(lParam);  // horizontal position of cursor
    int yPos = (signed short)HIWORD(lParam);  // vertical position of cursor
    unsigned int Index = GetWindowLong(hWnd, GWL_USERDATA);
    switch (message) {
    case WM_CREATE:
        _SetBitmapRegion(hWnd, _ahBmpDevice[1], _rgbTransparent, 1);
        break;
    case WM_PAINT:
        hdc = BeginPaint(hWnd, &ps); {
            HDC hdcImage = CreateCompatibleDC(hdc); {
                if (Index < COUNT_OF(_aHardkey)) {
                    SelectObject(hdcImage, _ahBmpDevice[_aHardkey[Index].IsPressed]);
                    BitBlt(hdc, 0, 0, 1000, 1000, hdcImage, _aHardkey[Index].x0, _aHardkey[Index].y0, SRCCOPY);
                }
            } DeleteDC(hdcImage);
        } EndPaint(hWnd, &ps);
        break;
        // Handle mouse events
    case WM_RBUTTONDOWN:
    { POINT Point;
    Point.x = xPos;
    Point.y = yPos;
    ClientToScreen(hWnd, &Point);
    TrackPopupMenu(_hMenuPopup, TPM_RIGHTBUTTON, Point.x, Point.y, 0, _hWndMain, NULL);
    }
    break;
    // Handle mouse events
    case WM_LBUTTONDOWN:
        SetCapture(hWnd);
        if (Index < COUNT_OF(_aHardkey)) {
            SIM_HARDKEY_CB* cb = _aHardkey[Index].pfCallback;
            if (_aHardkey[Index].Mode == 0) {
                _aHardkey[Index].IsPressed = 1;
            }
            else {
                _aHardkey[Index].IsPressed ^= 1;
            }
            if (cb) {
                (*cb)(Index, 1);
            }
            InvalidateRect(hWnd, NULL, TRUE);
        }
        break;
    case WM_LBUTTONUP:
        ReleaseCapture();
        if (Index < COUNT_OF(_aHardkey)) {
            SIM_HARDKEY_CB* cb = _aHardkey[Index].pfCallback;
            if (_aHardkey[Index].Mode == 0) {
                _aHardkey[Index].IsPressed = 0;
            }
            if (cb) {
                (*cb)(Index, 0);
            }
            InvalidateRect(hWnd, NULL, TRUE);
        }
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

/*********************************************************************
*
*       _WndProcLCDInfo
*/
static LRESULT CALLBACK _WndProcLCDInfo(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    HDC hDC;
    PAINTSTRUCT ps;
    int NumColors, DeltaMode;
    int xSize = 32;
    int ySize = 16;
    static U16 aBits[32 * 16];
    static BITMAPINFO BitmapInfo;
    static int BitmapCreated;
    unsigned int LayerIndex = GetWindowLong(hWnd, GWL_USERDATA);
    DeltaMode = LCD_GetDeltaModeEx(LayerIndex);
    if (DeltaMode) {
        NumColors = 1 << LCD_GetBitsPerPixel_L0Ex(LayerIndex);
    }
    else {
        NumColors = LCD_GetNumColorsEx(LayerIndex);
    }
    switch (message) {
    case WM_PAINT:
    {
        hDC = BeginPaint(hWnd, &ps); {
            if (NumColors <= 256) {
                int i;
                for (i = 0; i < NumColors; i++) {
                    int x0 = 10 * (i % 16);
                    int y0 = 2 + 10 * (i / 16);
                    int x1 = x0 + 8;
                    int y1 = y0 + 8;
                    HGDIOBJ hbPrev;
                    U32 Color = LCDSIM_Index2Color(i, LayerIndex);
                    HBRUSH hb = CreateSolidBrush(RGB(Color & 255,      // Red component
                        255 & (Color >> 8), // Green component
                        Color >> 16));    // Blue component
                    hbPrev = SelectObject(hDC, hb);
                    Rectangle(hDC, x0, y0, x1, y1);
                    SelectObject(hDC, hbPrev);
                    DeleteObject(hb);
                }
            }
            else {
                RECT Rect;
                if (!BitmapCreated) {
                    memset(&BitmapInfo, 0, sizeof(BITMAPINFO));
                    memset(aBits, 0, sizeof(aBits));
                    BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
                    BitmapInfo.bmiHeader.biWidth = 32;
                    BitmapInfo.bmiHeader.biHeight = 16;
                    BitmapInfo.bmiHeader.biPlanes = 1;
                    BitmapInfo.bmiHeader.biBitCount = 16;
                    BitmapInfo.bmiHeader.biCompression = BI_RGB;
                    {
                        int Color;
                        for (Color = 0; Color < 8; Color++) {
                            U8 r = (Color & 0x01) >> 0;
                            U8 g = (Color & 0x02) >> 1;
                            U8 b = (Color & 0x04) >> 2;
                            int x;
                            for (x = 0; x < xSize; x++) {
                                int xx = 31 - x;
                                int rr = r * xx;
                                int gg = g * xx;
                                int bb = b * xx;
                                U16 BlackValue = (rr << 10) + (gg << 5) + bb;
                                U16 WhiteValue = ((r ? 0x1f : x) << 10) + ((g ? 0x1f : x) << 5) + (b ? 0x1f : x);
                                int BlackIndex = Color * 2 * xSize + x;
                                int WhiteIndex = BlackIndex + xSize;
                                aBits[BlackIndex] = BlackValue;
                                aBits[WhiteIndex] = WhiteValue;
                            }
                        }
                    }
                    BitmapCreated = 1;
                }
                GetClientRect(hWnd, &Rect);
                SetStretchBltMode(hDC, COLORONCOLOR);
                StretchDIBits(hDC,            // hDC
                    0,              // DestX
                    0,              // DestY
                    Rect.right,     // nDestWidth
                    Rect.bottom,    // nDestHeight
                    0,              // SrcX
                    0,              // SrcY
                    xSize,          // wSrcWidth
                    ySize,          // wSrcHeight
                    aBits,          // lpBits
                    &BitmapInfo,    // lpBitsInfo
                    DIB_RGB_COLORS, // wUsage
                    SRCCOPY);
            }
        } EndPaint(hWnd, &ps);
    }
    break;
    case WM_TIMER:
        if (_aLUTModifyCnt[LayerIndex] != LCDSIM_GetModifyCntInfo(LayerIndex)) {
            _aLUTModifyCnt[LayerIndex] = LCDSIM_GetModifyCntInfo(LayerIndex);
            InvalidateRect(hWnd, NULL, FALSE);
        }
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

/*********************************************************************
*
*       _InitMenu
*/
static void _InitMenu(HMENU hMenu) {
    int EnableStop = (_THREAD_IsRunning() & !_THREAD_IsSuspended()) ? 0 : MF_GRAYED;
    int EnableContinue = (_THREAD_IsRunning() &  _THREAD_IsSuspended()) ? 0 : MF_GRAYED;
    EnableMenuItem(hMenu, ID_FILE_STOPAPPLICATION, MF_BYCOMMAND | EnableStop);
    EnableMenuItem(hMenu, ID_FILE_CONTINUEAPPLICATION, MF_BYCOMMAND | EnableContinue);
}

/*********************************************************************
*
*       _MainWnd_OnTimer
*/
static void _MainWnd_OnTimer(HWND hWnd) {
    static char acTitle[200];
    static char acTitleNew[200];
    strcpy(acTitleNew, BRANDING_GetAppNameLong());
    if (_THREAD_IsRunning()) {
        if (_THREAD_IsSuspended()) {
            strcat(acTitleNew, "(Suspended)");
        }
        else {
            strcat(acTitleNew, "(Executing)");
        }
    }
    else {
        strcat(acTitleNew, "(Terminated)");
    }
    if (strcmp(acTitle, acTitleNew)) {
        strcpy(acTitle, acTitleNew);
        SetWindowText(hWnd, acTitle);
    }
}

/*********************************************************************
*
*       _MainWnd_Paint
*/
static void _MainWnd_Paint(HWND hWnd) {
    PAINTSTRUCT ps;
    HDC hdc, hdcImage;
    hdc = BeginPaint(hWnd, &ps); {
        RECT r;
        GetClientRect(hWnd, &r);
        hdcImage = CreateCompatibleDC(hdc); {
            int x0, y0;
            HBITMAP hBitmap = LoadBitmap(_hInst, (LPCTSTR)IDB_LOGO);
            BITMAP Bitmap;
            GetObject(hBitmap, sizeof(Bitmap), &Bitmap);
            DeleteObject(hBitmap);
            x0 = (r.right - r.left - Bitmap.bmWidth) / 2;
            y0 = (r.bottom - r.top - Bitmap.bmHeight) / 2;
            SelectObject(hdcImage, _hLogo);
            BitBlt(hdc, x0, y0, Bitmap.bmWidth, Bitmap.bmHeight, hdcImage, 0, 0, SRCCOPY);
        } DeleteDC(hdcImage);
    } EndPaint(hWnd, &ps);
}


//! \brief      主窗口处理函数
//! \details    处理主窗口的各种消息
//!
//! \param[in]  hWnd   窗口句柄
//! \param[in]  message   窗口消息
//! \param[in]  wParam   参数1
//! \param[in]  lParam   参数2
//! \return     执行结果
static LRESULT CALLBACK _WndProcMain(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    int r = 0;
    _HandleKeyEvents(message, wParam);
    switch (message) {
    case WM_COMMAND:
        r = _MainWnd_Command(hWnd, message, wParam, lParam);
        break;
    case WM_PAINT: _MainWnd_Paint(hWnd); break;
    case WM_TIMER: _MainWnd_OnTimer(hWnd); break;
    case WM_CREATE:
        SetTimer(hWnd, 0, 20, NULL);
        break;
    case WM_DESTROY:
        KillTimer(hWnd, 0);
        PostQuitMessage(0);
        break;
    case WM_INITMENU: _InitMenu((HMENU)wParam); break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return r;
}

/*********************************************************************
*
*       _TranslateAccelerator

This function does basically the same thing as the WIN32 function
of the same name (without underscore)

*/
static int _TranslateAccelerator(HWND hWnd, HACCEL hAcc, MSG* pMsg) {
    if (pMsg->message == WM_KEYDOWN) {
        int i, NumAccels;
        ACCEL aAccel[20];
        NumAccels = CopyAcceleratorTable(hAcc, aAccel, 20);
        for (i = 0; i < NumAccels; i++) {
            int falt0 = (aAccel[i].fVirt & FALT) ? 1 : 0;
            int falt1 = pMsg->lParam & (1 << 29) ? 1 : 0;
            if ((falt0 == falt1) && (pMsg->wParam == (WPARAM)aAccel[i].key)) {
                SendMessage(hWnd, WM_COMMAND, aAccel[i].cmd, 0);
                return 1;   // Message handled
            }
        }
    }
    return 0;   // Message no handled
};

//! \brief      创建各个窗口并创建demo线程
//!
//! \return     执行结果
static int _WinMain_NoClean(HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow) {
    char *sErr;
    MSG msg;
    HACCEL hAccelTable;
    _pCmdLine = lpCmdLine;

    //检查是否为命令行
    if (strlen(lpCmdLine)) {
        _MessageBoxOnError = 0;
    }
    _timeStartup = timeGetTime();   //启动时时间
    _hInst = hInstance;
    _RegisterClasses();
    //
    // 加载资源
    //
    _hLogo = LoadImage(_hInst, (LPCTSTR)IDB_LOGO, IMAGE_BITMAP, 0, 0, 0);
    _ahBmpDevice[0] = (HBITMAP)LoadImage(_hInst, "Device.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    _ahBmpDevice[1] = (HBITMAP)LoadImage(_hInst, "Device1.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    if (_ahBmpDevice[0] == NULL)
        _ahBmpDevice[0] = (HBITMAP)LoadImage(_hInst, (LPCTSTR)IDB_DEVICE, IMAGE_BITMAP, 0, 0, 0);
    if (_ahBmpDevice[1] == NULL)
        _ahBmpDevice[1] = (HBITMAP)LoadImage(_hInst, (LPCTSTR)IDB_DEVICE + 1, IMAGE_BITMAP, 0, 0, 0);
    _hMenuPopup = LoadMenu(_hInst, (LPCSTR)IDC_SIMULATION_POPUP);
    _hMenuPopup = GetSubMenu(_hMenuPopup, 0);
    //
    // 初始化LCD模拟器
    //
    sErr = LCDSIM_Init();
    if (sErr) {
        _MessageBox(sErr);
        return 1;
    }
    /* Use device simulation or standard window */
  /*  if (_ahBmpDevice[0] && (_xPosLCD >= 0)) {
      BITMAP bmpDevice;
      GetObject(_ahBmpDevice[0], sizeof(bmpDevice), &bmpDevice);
      _hWndMain = CreateWindowEx(0, // Extended style -> Use WS_EX_TOPMOST if you want window to stay on top
                                acClassNameDevice, "Target device",
                                WS_CLIPCHILDREN | WS_POPUP| WS_VISIBLE,
                                10, 20, bmpDevice.bmWidth, bmpDevice.bmHeight, 0, NULL, _hInst, NULL);
    } else {
      _hWndMain = CreateWindow(acClassNameMain, BRANDING_GetAppNameLong(),
                               WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_VISIBLE, CW_USEDEFAULT, 0,
                               _GetXSizePhys() + 250,
                               _GetYSizePhys() + 150,
                               NULL, NULL, hInstance, NULL);
      if (!_hWndMain)
        return FALSE;
      _CreateWndLCD();
      WinLOG_Create(_hInst, _hWndMain);
    }*/
    //////houhh 20061023...
    // 创建各个显示窗口
    //
    _hWndMain = CreateWindow(acClassNameMain, BRANDING_GetAppNameLong(),
        WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_VISIBLE, CW_USEDEFAULT, 0,
        _GetXSizePhys() + 250,
        _GetYSizePhys() + 150,
        NULL, NULL, hInstance, NULL);
    if (!_hWndMain)
        return FALSE;
    // 创建LCD模拟窗口和log日志记录窗口
    _CreateWndLCD();
    WinLOG_Create(_hInst, _hWndMain, _GetYSizePhys() + 30);
    //////
    ShowWindow(_hWndMain, 1);

    //
    // 开始线程和消息循环
    //
    _THREAD_StartApplication();
    hAccelTable = LoadAccelerators(hInstance, (LPCTSTR)IDC_SIMULATION);
    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0)) {
        if (!_TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }
    _THREAD_KillAll();      // 关闭所有后台线程
    return msg.wParam;
}

/*********************************************************************
*
*       SIM_HARDKEY_ ... functions
*
**********************************************************************

The following routines are available for the simulated application.
The routines are "C" linked.

*/
/*********************************************************************
*
*       SIM_HARDKEY_GetState
*/
int  SIM_HARDKEY_GetState(unsigned int i) {
    if (i > COUNT_OF(_aHardkey))
        return 0;
    return _aHardkey[i].IsPressed;
}

/*********************************************************************
*
*       SIM_HARDKEY_SetState
*/
int  SIM_HARDKEY_SetState(unsigned int i, int State) {
    int r;
    if (i > COUNT_OF(_aHardkey)) {
        return 0;
    }
    r = _aHardkey[i].IsPressed;
    _aHardkey[i].IsPressed = State;
    return r;
}

/*********************************************************************
*
*       SIM_HARDKEY_GetNum
*/
int  SIM_HARDKEY_GetNum(void) {
    return _NumHardkeys;
}

/*********************************************************************
*
*       SIM_HARDKEY_SetCallback
*/
SIM_HARDKEY_CB*  SIM_HARDKEY_SetCallback(unsigned int KeyIndex, SIM_HARDKEY_CB* pfCallback) {
    SIM_HARDKEY_CB* r;
    if (KeyIndex > COUNT_OF(_aHardkey)) {
        return 0;
    }
    r = _aHardkey[KeyIndex].pfCallback;
    _aHardkey[KeyIndex].pfCallback = pfCallback;
    return r;
}

/*********************************************************************
*
*       SIM_HARDKEY_SetMode
*/
int  SIM_HARDKEY_SetMode(unsigned int KeyIndex, int Mode) {
    int r;
    if (KeyIndex > COUNT_OF(_aHardkey)) {
        return 0;
    }
    r = _aHardkey[KeyIndex].Mode;
    _aHardkey[KeyIndex].Mode = Mode;
    return r;
}

/*********************************************************************
*
*       SIM_ ... functions
*
**********************************************************************

The following routines are available for the simulated application.
The routines are "C" linked.

*/
// 设置透明色
int SIM_SetTransColor(int Color) {
    int r = _rgbTransparent;
    _rgbTransparent = Color;
    return r;
}

/*********************************************************************
*
*       SIM_SetLCDColorWhite
*/
int SIM_SetLCDColorWhite(unsigned int Index, int Color) {
    int r = 0;
    if (Index < COUNT_OF(LCDSIM_aLCDColorWhite)) {
        r = LCDSIM_aLCDColorWhite[Index];
        LCDSIM_aLCDColorWhite[Index] = Color;
    }
    return r;
}

/*********************************************************************
*
*       SIM_SetLCDColorBlack
*/
int SIM_SetLCDColorBlack(unsigned int Index, int Color) {
    int r = 0;
    if (Index < COUNT_OF(LCDSIM_aLCDColorBlack)) {
        r = LCDSIM_aLCDColorBlack[Index];
        LCDSIM_aLCDColorBlack[Index] = Color;
    }
    return r;
}


// 设置xy方向放大系数
void SIM_SetMag(int MagX, int MagY) {
    if (_MagX != 0) {
        _MagX = MagX;
    }
    if (_MagY != 0) {
        _MagY = MagY;
    }
}

/*********************************************************************
*
*       SIM_GetMagX, SIM_GetMagY
*/
int      SIM_GetMagX(void) { return _MagX; }
int      SIM_GetMagY(void) { return _MagY; }

//! \brief      设置模拟LCD显示在位图中的起始位置
//! \param[in]  x   x坐标
//! \param[in]  y   y坐标
//! \return     void
void  SIM_SetLCDPos(int x, int y) {
    _xPosLCD = x;
    _yPosLCD = y;
}

// 获取从启动到当前的时间间隔
//
int SIM_GetTime(void) { return timeGetTime() - _timeStartup; }

// 仿真器延时，单位ms
//
void SIM_Delay(int ms) {
    _THREAD_Sleep(ms);
    SIM_GetTime();
}

// 仿真器空闲任务
//
void SIM_ExecIdle(void) {
    _THREAD_Sleep(1);
}

// 等待按键按下
//
int SIM_WaitKey(void) {
    int r;
    SIM_Log("\nSIM_WaitKey()");
    while (_KeyBuffer == 0) {
        GUI_Delay(10);
    }
    r = _KeyBuffer;
    _KeyBuffer = 0;
    SIM_Log(" Done.");
    return r;
}

// 获取按键
//
int SIM_GetKey(void) {
    int r;
    if ((r = _KeyBuffer) != 0)
        _KeyBuffer = 0;
    return r;
}

/*********************************************************************
*
*       SIM_StoreKey
*/
void SIM_StoreKey(int Key) {
    if (!_KeyBuffer)
        _KeyBuffer = Key;
}

//! \brief      模拟器日志记录
//! \details    调用该函数将日志信息和产生事件记录起来
//!
//! \param[in]  s    日志信息
//! \return     void
void SIM_Log(const char *s) {
    OutputDebugString(s);
    _LogTime();
    _LOG_Add(s);
}

/*********************************************************************
*
*       SIM_Warn
*/
void SIM_Warn(const char *s) {
    _LogTime();
    _LOG_AddRed();
    _LOG_Add(s);
}

//! \brief    模拟器错误输出
//! \param[in]  s 错误字符串
//! \return   void
void SIM_ErrorOut(const char *s) {
    static int Cnt;
    _LogTime();
    _LOG_AddRed();
    _LOG_Add(s);
    if (_MessageBoxOnError) {
        Cnt++;
        _MessageBox(s);
    }
    _SendToErrorFile(s);
}

/*********************************************************************
*
*       SIM_EnableMessageBoxOnError
*/
void SIM_EnableMessageBoxOnError(int Status) {
    _MessageBoxOnError = Status;
}

/*********************************************************************
*
*       SIM_GetCmdLine
*/
const char * SIM_GetCmdLine(void) {
    return _pCmdLine;
}

/*********************************************************************
*
*       SIM_CreateTask
*/
void SIM_CreateTask(char * pName, void * pFunc) {
    if (_NumTask < COUNT_OF(_ahThread)) {
        _ahThread[_NumTask] = CreateThread(NULL, 0, _CreateTask, pFunc, CREATE_SUSPENDED, &_aThreadID[_NumTask]);
        SetThreadPriority(_ahThread[_NumTask], THREAD_PRIORITY_LOWEST);
        ResumeThread(_ahThread[_NumTask]);
        _NumTask++;
    }
}

/*********************************************************************
*
*       SIM_Start
*/
void SIM_Start(void) {
    while (1)
        Sleep(10);
}

//! \brief    winmain win窗口程序主函数
//! \param[in]  hInstance   实例句柄已被省略
//! \param[in]  hPrevInstance   上个句柄
//! \param[in]  lpCmdLine   命令行字符串
//! \param[in]  nCmdShow   运行时的显示方式
//! \return
#if (SIM_WINMAIN)
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    int r;
    //底层初始化并创建各个窗口
    SIM_X_Init();
    r = _WinMain_NoClean(hInstance, hPrevInstance, lpCmdLine, nCmdShow);

    //关闭并清除窗口和对象
    if (_hWndLCD1)
        DestroyWindow(_hWndLCD1);
    if (_ahBmpDevice[0])
        DeleteObject(_ahBmpDevice[0]);
    if (_ahBmpDevice[1])
        DeleteObject(_ahBmpDevice[1]);
    if (_hLogo)
        DeleteObject(_hLogo);
    if (_hFileError)
        CloseHandle(_hFileError);
    return r;
}
#endif

/*************************** End of file ****************************/
